#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G25 -- S07 ------------------------------------------------------------##
__all__ = []

from .. import autostacks
from .. import formlib as layout
from .. import pilelib as stack
from .. import stackfill as fill
from ..gamelib import info as gi, state, game
from ..hint import CautiousDefaultHint, DropbyDefaultHint
from ..packlib import info as cs, place

GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper
ClickMethods = stack.SingleAsDoubleClickMethod


class Matrimony_Talon(stack.RedealTalon):
    """
    Deal 1 card on _row_. 1st turn reset all with next turns just pile [15..0]
    """

    def deal_cards(self, sound=False):
        """
        deal_cards([sound:bool=False]) -> int

        Actual dealing, called from game.talon_deal(). Overide default

        :keyword boolean sound: sound fx?
        :keyword boolean shuffle: shuffle cards before redealing?
        :keyword boolean reverse: deals cards in reverse order?
        :return: number of cards dealt.
        """
        rows, rounds = self.game.s['rows'], self.round
        if sound and self.game.can_play_sound:
            self.game._play_sound('deal')
        num_cards = 0
        if not self.cards:
            num_cards += self._redeal(rows=rows[-rounds])
        if self.round == 1:
            num_cards += self.dealRowAvail(sound=False)
        else:
            num_cards += self.dealRowAvail(rows=rows[-rounds + 1:],
                                           sound=False)
            while self.cards:
                num_cards += self.dealRowAvail(rows=rows, sound=False)
        self.game._stop_sound()
        return num_cards


class Adela_Foundation(stack.AbstractFoundation):
    """
    Build 2nd 8 after base of 1st 8 plays above. (eg 1 has base, 9 may build.)
    """

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:tup) -> bool

        Check parent can build with cards from from_stack. Cards must be on
        _foundation_ of same suit in first set of 8.

        :param object from_stack: pile playing cards.
        :param tuple cards: cards playing on pile.
        :return: pile can build with cards from played from_stack?
        :rtype: boolean

        seealso::
          Method pysollib.stackcaps.builds for the *capability* check.
        """
        if not super(Adela_Foundation, self).cardsBuild(from_stack, cards):
            return False
        foundations = self.game.s['foundations']
        return foundations[foundations.index(self) % 8].cards


class GapWaste_RK_RowStack(stack.GapFromWasteBuildMethod, stack.RK_RowStack):
    """
    Build down in rank alone. Play top. Base from _waste_.
    """
    _BOTTOM_IMAGE_ = 'shade'


class AC_RowStack(stack.AC_RowStack):
    """
    Build down in rank by alt color. _BOTTOM_IMAGE_ is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'


class AceFoundation(stack.AbstractFoundation):
    """
    Build any. Play top. _BOTTOM_IMAGE_ is rank.
    """
    _BOTTOM_IMAGE_ = 'rank'


class DropRowStack(ClickMethods, stack.BasicRowStack):
    """
    Build none. Play top.
    """
    pass


class DropReserve(ClickMethods, stack.OpenStack):
    """
    Build none. Play top.
    """
    pass


class Sultan(game.Game):
    """
    Finish cards on _foundation_ within 3 turn.

    _rows_
      Pile 6 over 2 col W E. Init deal a card. Build 1 card. Play top.

    _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 12 over 3 row center. Init base KING with ACE hearts and KING
      hearts center. Build up in rank by same suit wrap. Center KING build
      none. Play top.

    note::
      ⟪autodrop⟫ disabled

    seealso::
      <http://en.wikipedia.org/wiki/Sultan_%28solitaire%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (mod=13,
      base_rank=KING), stack.SS_Foundation (suit=2,max_cards=1,
      base_rank=KING), stack.SS_Foundation (suit=2, max_cards=12)
    :cvar Stack _rows_: stack.ReserveStack
    """
    __info__ = dict(id=330, name='Sultan', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.90fc',
                    altnames=('Sultan of Turkey',))
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=3)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.SS_Foundation, mod=13, base_rank=cs.KING),
                     Wrap(stack.SS_Foundation, suit=2, max_cards=1,
                          base_rank=cs.KING),
                     Wrap(stack.SS_Foundation, suit=2, max_cards=12),)
    _rows_ = stack.ReserveStack

    def _create_game(self, reserves=6, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + 5 * l.XS,
                     l.YM + max(reserves / 2, 4) * l.YS + l.TEXT_HEIGHT)
        ddx = 2 * l.XM + l.XS
        for i, suit, dx, dy in ((0, 0, 0, 0), (0, 1, 0, 1), (0, 2, 2, 2),
                                (0, 3, 0, 2), (0, 0, 2, 0), (0, 1, 2, 1),
                                (1, 2, 1, 2), (0, 3, 2, 2), (2, 2, 1, 0),):
            x, y = l.delta_coords(x=ddx, dx=dx, dy=dy)
            s['foundations'].append(self._foundations_[i](x, y, game=self,
                                 suit=suit))
        x = l.XM
        for i in range(2):  # Horz
            y = l.YM
            for j in range(reserves / 2):  # Vert
                s['rows'].append(self._reserve_(x, y, game=self))
                y += l.YS
            x += self.width - l.XS
        x, y = self.width / 2 - l.XS, self.height - l.YS - l.TEXT_HEIGHT
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        l.set_rounds_text(s['talon'], 'sss')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 's')
        l.defaultStackGroups()
        return l

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE heart and KING on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(place.MoveToTop(cards,
                                        place.OneAceHeart), place.AllKing)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autodrop.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.flip_quickplay(super(Sultan, self)._autopiles(event))


class SultanPlus(Sultan):
    """
    Finish cards on _foundation_ within 3 turn.

    _rows_
      Pile 8 over 2 col W E. Init deal a card. Build 1 card. Play top.

    _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 12 over 3 row center. Init base KING with ACE hearts and KING
      hearts center. Build up in rank by same suit wrap. Center KING build
      none. Play top.

    note::
      ⟪autodrop⟫ disabled

    seealso::
      <http://en.wikipedia.org/wiki/Sultan_%28solitaire%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (suit=2, max_cards=12,
      mod=13), stack.SS_Foundation (suit=2, max_accept=0, max_cards=1,
      mod=13), stack.SS_Foundation (mod=13)
    :cvar Stack _rows_: stack.ReserveStack
    """
    __info__ = dict(id=331, name='Sultan +', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('reserves', 8)
        return super(SultanPlus, self)._create_game(**kw)


class CaptiveQueens(game.Game):
    """
    Finish cards on _foundation_ within 3 turn.

    _talon_
      Pile 1 NW. Deal 1 card on _waste_.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 12 over 3 row. Build in rank by same suit wrap: N up from base
      FIVE and S down from base SIX. Center build 1 QUEEN. Play top, except
      center.

    seealso::
      <http://en.wikipedia.org/wiki/Captive_Queens>
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (mod=13, max_cards=6,
      base_rank=FIVE, dir=-1), stack.AbstractFoundation (max_cards=1,
      max_move=0, base_rank=QUEEN), stack.SS_Foundation (mod=13,
      max_cards=6, base_rank=SIX)
    """
    __info__ = dict(id=410, name='Captive Queens', game_type=GT.ONE_DECK,
                    decks=1, redeals=2, skill_level=SL.MOSTLY_LUCK,
                    altnames=('Quadrille',), version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=3)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.SS_Foundation, mod=13, max_cards=6,
                          base_rank=cs.FIVE, dir=-1),
                     Wrap(stack.AbstractFoundation, max_cards=1, max_move=0,
                          base_rank=cs.QUEEN),
                     Wrap(stack.SS_Foundation, mod=13, max_cards=6,
                          base_rank=cs.SIX),)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        cols = len(self._foundations_)
        self.table_size = (l.XM + 11 * l.HXS, l.YM + cols * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        l.set_rounds_text(s['talon'], 'nn')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        dx, y = x + 3 * l.XS / 2, l.YM
        for i in l.SUIT:  # Vert
            x = dx
            for _foundation_ in self._foundations_:  # Horz
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.

        .. \_start([]) -> None
        """
        pass


class Boudoir(CaptiveQueens):
    """
    Finish cards on _foundation_ within 3 turn.

    _talon_
      Pile 1 NW. Deal 1 card on _waste_.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 16 over 3 row. Init 3rd base QUEEN. Build in rank by same suit:
      1st up and 4th down from base TEN. 2nd build JACK. 3rd build QUEEN. Play
      top except 2nd and 3rd.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation, max_cards=1, max_move=0, base_rank=QUEEN),
      stack.SS_Foundation (max_cards=1, max_move=0, base_rank=JACK),
      stack.SS_Foundation (mod=13, max_cards=11, base_rank=TEN, dir=-1)
    """
    __info__ = dict(id=354, name='Boudoir', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, max_cards=1, max_move=0,
                          base_rank=cs.QUEEN),
                     Wrap(stack.SS_Foundation, max_cards=1, max_move=0,
                          base_rank=cs.JACK),
                     Wrap(stack.SS_Foundation, mod=13, max_cards=11, dir=-1,
                          base_rank=cs.TEN),)

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return QUEEN of suit on top by suit, deck for _foundation_
        deal.
        """
        return place.MoveToTop(cards, place.OneQueen)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations][8:12] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'][8:12])
        super(Boudoir, self)._start(**kw)


class Contradance(game.Game):
    """
    Finish cards on _foundation_ within 2 turn.

    _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 16 over 2 row. Init deal N base FIVE and S deal base SIX. Build in
      rank by same suit wrap: top up from base FIVE and bottom down from base
      SIX. Play top.

    seealso::
      <http://en.wikipedia.org/wiki/Contradance_%28solitaire%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (mod=13, max_cards=6,
      base_rank=FIVE, dir=-1, mod=13), stack.SS_Foundation (mod=13,
      max_cards=7, base_rank=SIX)
    """
    __info__ = dict(id=418, name='Contradance', game_type=GT.TWO_DECK,
                    decks=2, redeals=1, skill_level=SL.LUCK,
                    altnames=('Cotillion',), version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=2)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.SS_Foundation, base_rank=cs.FIVE, dir=-1,
                          mod=13, max_cards=6),
                     Wrap(stack.SS_Foundation, base_rank=cs.SIX, max_cards=7),)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + l.NSUITS * l.XS, l.YM + 4 * l.YS)
        x = l.XM
        for i in l.SUITS:  # Horz
            y = l.YM
            for _foundation_ in self._foundations_:  # Vert
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                y += l.YS
            x += l.XS
        x = self.width / 2 - l.XS
        y += l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        l.set_rounds_text(self.s['talon'], 'nnn')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'n')
        l.defaultStackGroups()
        return l

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return FIVE and SIX on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllFiveSix)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'])


class IdleAces(game.Game):
    """
    Finish cards on _foundation_ within 3 turn.

    _talon_
      Pile 1 NW. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 12 cross arm and corner. Init deal outer base KING and inner base
      TWO. Build in rank by same suit wrap: outer down from base KING and
      inner up from base TWO. Corner build ACE. Play top except corner.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.RK_Foundation (base_rank=TWO,
      max_move=0, max_cards=12), stack.RK_Foundation (base_rank=KING,
      dir=-1, max_move=0), AceFoundation (max_cards=1, max_move=0,
      base_rank=ACE)
    """
    __info__ = dict(id=419, name='Idle Aces', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=3)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.RK_Foundation, base_rank=cs.TWO, max_move=0,
                          max_cards=12),
                     Wrap(stack.RK_Foundation, base_rank=cs.KING, dir=-1,
                          max_move=0),
                     Wrap(AceFoundation, max_cards=1, max_move=0,
                          base_rank=cs.ACE),)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 7 * l.XS, l.YM + 4 * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        l.set_rounds_text(s['talon'], 'ne', dx=l.XS)
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 's')
        for _foundation_, coords in zip(self._foundations_[:2],
                                       (((4, 2), (5, 5), (4, 4), (3, 5)),
                                        ((4, 0), (6, 5), (4, 6), (2, 5)))):
            for dx, dy in coords:  # Outer and Inner Arms
                x, y = l.delta_coords(dx=dx, dy=dy, fy=2)
                s['foundations'].append(_foundation_(x, y, game=self))
        _foundation_ = self._foundations_[-1]
        for suit, dx, dy in zip(l.SUIT, (9, 11, 9, 11), (3, 3, 7, 7)):
            x, y = l.delta_coords(dx=dx, dy=dy, fx=2, fy=2)
            s['foundations'].append(_foundation_(x, y, game=self, suit=suit))
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return TWO and KING of suit on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneTwoKing)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations][:8] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'][:8])


class LadyOfTheManor(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 S _reserve_. Init deal 12 cards. Build none. Play top.

    _reserve_
      Pile 8 piles N and 4 over 2 col W E. Init deal last cards with rank
      sorted by pile. Build none. Play top.

    _foundation_
      Pile 8 S. Init deal base ACE. Build up in rank alone. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: DropRowStack
    :cvar Stack _reserve_: DropReserve
    """
    __info__ = dict(id=423, name='Lady of the Manor', game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_LUCK,
                    altnames=('Vassal', 'La Chatelaine'), version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.RK_Foundation
    _row_ = DropRowStack
    _reserve_ = DropReserve

    def _create_game(self, playcards=14, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + l.NSUITS * l.XS,
                     l.YM + max(4 * l.YS, 2 * l.YS, l._get_stack_height(playcards, l.YS)),)
        x, y = l.XM, self.height - l.YS
        for i in l.SUITS:  # Horz
            s['foundations'].append(self._foundations_(x, y, game=self))
            x += l.XS
        x, y = self.width / 2 - l.NSUITS / 4 * l.XS, l.YM + l.YS
        for i in range(l.NSUITS / 2):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        y = l.XM
        for i in range(3):  # Vert
            for j in (range(l.NSUITS) if i == 0 else (0, l.NSUITS)):  # Horz
                x = l.XM + j * l.XS
                s['reserves'].append(self._reserve_(x, y, game=self))
            y += l.YS
        x, y = self.width - l.XS, self.height - 2 * l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultAll()
        return l

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=12, flip=False, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 12 ea top.
          - s[reserves] remain. by rank.

        .. \_start([rows=12, flip=False]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0, flip=flip)
        self.s['talon'].dealRow()
        while self.s['talon'].cards:
            self.s['talon'].dealRow(
                     rows=[self.s['reserves'][self.s['talon'].TOPCARD['rank']]])


class Matrimony(game.Game):
    """
    Finish cards on _foundation_ within 17 turn.

    _row_
      Pile 16 over 2 row S. Init deal 1 card. Build none. Play top.

    _talon_
      Pile 1 NW. Deal 1 card on _row_. 1st turn reset all with next turns just
      pile [15 .. 0].

    _foundation_
      Pile 8 N. Init deal N base Jack diamonds, S base QUEEN diamonds. Build
      in rank by same suit wrap: N down from base JACK, S up from base
      QUEEN. Play top.
    ------
    :cvar Stack _talon_: Matrimony_Talon (max_rounds=17)
    :cvar Stack _foundation_: stack.SS_Foundation (base_rank=JACK,
      dir=-1, mod=13), stack.SS_Foundation (base_rank=QUEEN, dir=1,
      mod=13)
    :cvar Stack _row_: DropRowStack
    """
    __info__ = dict(id=424, name='Matrimony', game_type=GT.TWO_DECK, decks=2,
                    redeals=16, skill_level=SL.MOSTLY_LUCK, version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(Matrimony_Talon, max_rounds=17)
    _foundations_ = (Wrap(stack.SS_Foundation, base_rank=cs.JACK, dir=-1,
                          mod=13),
                     Wrap(stack.SS_Foundation, base_rank=cs.QUEEN, dir=1,
                          mod=13),)
    _row_ = DropRowStack

    def _create_game(self, rows=16, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 4 * l.YS)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        l.set_rounds_text(s['talon'], 'ne')
        dx = x + 2 * l.XS
        for _foundation_ in self._foundations_:
            x = dx
            for i in l.SUIT:
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                x += l.XS
            y += l.YS
        for i in range(2):
            x = l.XM
            for j in range(rows / 2):
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return JACK of suit and QUEEN diamond on top by rank, suit,
        deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneJackQueenDiamonds)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations][3::4] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'][3::4], frames=0)
        self.s['talon'].dealRow()


class PicturePatience(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 9 over 3 row. Init deal 1 card. Build 1 card. Play top. Gap deal 1
      card from _waste_.

     _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 over 2 col W E. Build in rank by same suit wrap: W up and E down.
      Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=1)
    :cvar Stack _row_: stack.BasicRowStack (max_accept=1)
    """
    __info__ = dict(id=598, name='Picture Patience', game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_LUCK,
                    rules_filename='patriarchs', version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = Wrap(stack.BasicRowStack, max_accept=1)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + 5 * l.XS,
                     l.YM + 4 * l.YS + l.TEXT_HEIGHT)
        x = l.XM
        for _foundation_ in self._foundations_:
            y = l.YM
            for i in l.SUIT:
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                y += l.YS
            x += 2 * l.XM + 4 * l.XS
        y = l.YM
        for i in range(3):  # Vert
            x = 2 * l.XM + l.XS
            for j in range(3):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = self.width / 2 - l.XS, self.height - l.YS - l.TEXT_HEIGHT
        s['talon'] = self._talon_(x, y, game=self)
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_rounds_text(s['talon'], 'sss')
        anchors = ('sw', 'se',) if s['talon'].max_rounds < 2 else ('s',) * 2
        l.set_ncards_text(s['talon'], anchors[0])
        l.set_ncards_text(s['wastes'][-1], anchors[1])
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])


class Patriarchs(PicturePatience):
    """
    Finish cards on _foundation_ within 2 turn.

    _row_
      Pile 9 over 3 row. Init deal 1 card. Build 1 card. Play top. Gap deal 1
      card from _waste_.


     _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 over 2 col W E. Build in rank by same suit wrap: W up and E down.
      Play top.

    seealso::
      <http://en.wikipedia.org/wiki/Patriarchs_%28solitaire%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=1)
    :cvar Stack _row_: stack.BasicRowStack (max_cards=1, max_accept=1)
    """
    __info__ = dict(id=429, name='Patriarchs', game_type=GT.TWO_DECK, decks=2,
                    redeals=1, skill_level=SL.MOSTLY_LUCK, version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=2)

    def  _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()


class SixesAndSevens(game.Game):
    """
    Finish cards on _foundation_ within 2 turn.

    _row_
      Pile 9 over 3 row. Init deal 1 card. Build 1 card. Play top. Gap deal 1
      card from _waste_.

     _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 16 over 4 row W. Init deal 1st 2nd base SEVEN and 3rd 4th base SIX.
      Build in rank by same suit wrap: 1st 2nd up base SEVEN and 3rd 4th down\
      base SIX. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=1)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (base_rank=SIX, dir=-1,
      max_cards=6), stack.SS_Foundation (base_rank=SEVEN, max_cards=7)
    :cvar Stack _row_: stack.ReserveStack
    """
    __info__ = dict(id=438, name='Sixes and Sevens', game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_LUCK,
                    version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=1)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.SS_Foundation, base_rank=cs.SEVEN, max_cards=7),
                     Wrap(stack.SS_Foundation, base_rank=cs.SIX, dir=-1,
                          max_cards=6),)
    _row_ = stack.ReserveStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 4 * l.YS)
        y = self.height
        for _foundation_ in self._foundations_:
            for i in l.NDECKS:  # Vert
                x = l.XM
                for j in l.SUIT:  # Horz
                    s['foundations'].append(_foundation_(x, y, game=self,
                                                      suit=j))
                    x += l.XS
                y -= l.YS
        dx, y = x + l.XS, l.YM
        for i in range(3):  # Vert
            x = dx
            for j in range(3):  # Horz
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x = dx
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'sw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return SIX and SEVEN on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllSixSeven)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()


class TwoRings(game.Game):
    """
    Finish cards on _foundation_ within 2 turn.

    _row_
      Pile 8 S _foundation_. Init deal 1 card. Build 1 card. Play top. Gap
      deal 1 card from _waste_.

     _talon_
      Pile 1 E _row_. Deal 1 card on _row_.

    _foundation_
      Pile 8 2 circle. Init deal W base SEVEN and E base SIX. Build in rank by
      same suit wrap: W up base SEVEN and E down base SIX. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowRedealTalon (max_rounds=1)
    :cvar Stack _foundation_: stack.SS_Foundation (base_rank=SIX, dir=-1,
      max_cards=6), stack.SS_Foundation (base_rank=SEVEN, max_cards=7)
    :cvar Stack _row_: stack.ReserveStack
    """
    __info__ = dict(id=729, name='Two Rings', game_type=GT.TWO_DECK, decks=2,
                    redeals=1, skill_level=SL.MOSTLY_LUCK, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.DealRowRedealTalon, max_rounds=2)
    _foundations_ = (Wrap(stack.SS_Foundation, base_rank=cs.SEVEN, max_cards=7),
                     Wrap(stack.SS_Foundation, base_rank=cs.SIX, dir=-1,
                          max_cards=6),)
    _row_ = stack.BasicRowStack

    def _create_game(self, rows=8, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + (rows + 2) * l.XS, l.YM + 5 * l.YS)
        ddx = self.width / 2
        for _foundation_ in self._foundations_:
            for suit, dx, dy in zip(l.SUITS, (5, 7, 8, 7, 5, 3, 2, 3),
                                             (0, 1, 4, 7, 8, 7, 4, 1)):
                x, y = l.delta_coords(x=ddx, dx=dx, dy=dy, fx=2, fy=3)
                s['foundations'].append(_foundation_(x, y, game=self,
                                                     suit=suit))
            ddx = l.XM
        x, y = l.XM, l.YM + 4 * l.YS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            x += l.XS
        x += l.XS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'nw')
        l.set_rounds_text(s['talon'], 'sw')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return SIX and SEVEN on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllSixSeven)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()


class CornerSuite(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 9 over 3 row. Init 1 card. Build down in rank alone. Play top. Base
      from _waste_.

     _talon_
      Pile 1 N. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 4 NW NE SE SW _rows_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowRedealTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: GapWaste_RK_RowStack (max_move=1)
    :cvar Hint __hint__: hint.CautiousDefaultHint
    """
    __info__ = dict(id=477, name='Corner Suite', game_type=GT.TWO_DECK,
                    decks=1, redeals=0, skill_level=SL.MOSTLY_LUCK,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(GapWaste_RK_RowStack, max_move=1)
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 5 * l.XS, l.YM + 5 * l.YS)
        for suit, dx, dy in zip(l.SUITS, (0, 4,) * 2, (0,) * 2 + (4,)):
            x, y = l.delta_coords(dx=dx, dy=dy)
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
        x, y = self.width / 2 - l.XS, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'nw')
        x += l.XS
        s['wastes'][-1].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        y = l.YM + l.YS
        for i in range(3):  # Vert
            x = l.XM + l.XS
            for j in range(3):  # Horz
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.

        .. \_start([]) -> None
        """
        pass


class Marshal(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 24 over 4 row. Init deal 1 card. Build either in rank alone. Play
      top. Gap deal 1 card from _talon_. Base none.

     _talon_
      Pile 1 S. Deal 1 card on _row_.

    _foundation_
      Pile 8 over 2 col W E _rows_. Build in rank by same suit: W up and W
      down. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1),
    :cvar Stack _row_: stack.UD_SS_RowStack (base_rank=GAP)
    :cvar Hint __hint__: hint.DropbyDefaultHint
    """
    __info__ = dict(id=559, name='Marshal', game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _foundation_ = (stack.SS_Foundation,
                    Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = Wrap(stack.UD_SS_RowStack, base_rank=cs.GAP)
    _hint_ = DropbyDefaultHint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 9 * l.XS, l.YM + 5 * l.YS)
        x = l.XM
        for _foundation_ in self._foundations_:  # Horz
            y = l.YM
            for i in l.SUIT:  # Vert
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                y += l.YS
            x = self.width - l.XS
        x, y = (self.width - l.XS) / 2, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        y = l.YM
        for i in range(4):  # Vert
            x = l.XM + 3 * l.XS / 2
            for j in range(6):  # Horz
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class RoyalAids(game.Game):
    """
    Finish cards on _foundation_ without limit turn.

    _row_
      Pile 2. Build either in rank by alt color. Play top. Base KING.

    _reserve_
      4 pile S _row_. Init deal 10 card. Play top.

     _talon_
      Pile 1 W. Deal 1 card on _waste_.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 2 cross of 4 N _row_. Init deal base ACE. Build up in rank by alt
      color. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=INFINITE)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.AC_Foundation
    :cvar Stack _row_: AC_RowStack (max_move=1, base_rank=KING)
    :cvar Stack _reserve_: stack.BasicRowStack
    :cvar Hint __hint__: hint.CautiousDefaultHint
    """
    __info__ = dict(id=565, name='Royal Aids', game_type=GT.TWO_DECK,
                    decks=2, redeals=cs.INFINITE, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=cs.INFINITE)
    _waste_ = stack.WasteStack
    _foundation_ = stack.AC_Foundation
    _row_ = Wrap(AC_RowStack, max_move=1, base_rank=cs.KING)
    _reserve_ = stack.BasicRowStack
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 4 * l.YS + l.TEXT_HEIGHT)
        center = lambda p: (self.width - p * l.XS) / 2
        ddx = l.XM + 3 * l.XS / 2
        for k in range(2):
            for suit, dx, dy in zip(l.SUIT, (1, 0, 2, 1), (0, 1, 1, 2)):
                x, y = l.delta_coords(x=ddx, dx=dx, dy=dy, fy=2)
                s['foundations'].append(self._foundation_(x, y, game=self,
                                                       suit=suit))
                ddx += 2 * l.XS
        x, y = l.XM, l.YM + l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'se')
        x = center(2)
        for i in range(2):
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            x += l.XS
        x = center(4)
        y += l.YS
        for i in range(4):
            s['reserves'].append(self._reserve_(x, y, game=self))
            l.set_ncards_text(s['reserves'][i], 's')
            x += l.XS
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, reserves=10, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[reserves] 10 ea.

        .. \_start([reserves=10]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(reserves - 4):
            self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        for i in range(4):
            self.s['talon'].dealRow(rows=self.s['reserves'])


class CircleEight(game.Game):
    """
    Finish cards on _foundation_ within 2 turn.

    _row_
      Pile 8 circle around _talon_ and waste_. Init deal 1 card. Build up in
      rank alone wrap. Play none.

     _talon_
      Pile 1. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _row_: stack.RK_RowStack (max_move=0, dir=1, mod=13)
    """
    __info__ = dict(id=635, name='Circle Eight', game_type=GT.ONE_DECK,
                    decks=1, redeals=1, skill_level=SL.MOSTLY_LUCK,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=2)
    _waste_ = stack.WasteStack
    _row_ = Wrap(stack.RK_RowStack, dir=1, mod=13, max_move=0)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 5 * l.XS, l.YM + 4 * l.YS)
        for dx, dy in ((1, 0), (2, 0), (3, 0), (4, 3), (3, 6), (2, 6), (1, 6),
                       (0, 3),):
            x, y = l.delta_coords(dx=dx, dy=dy, fy=2)
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
        x, y = self.width / 2 - l.XS, (self.height - l.YS) / 2
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'nw')
        l.set_rounds_text(s['talon'], 'nn')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @property
    def is_complete(self):
        return not any(self.s['talon'].cards, self.s['wastes'][-1].cards)


class Adela(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 9. Init deal 1 card. Build up in rank by same suit. Play top. Gap
      deal 1 card from _talon_.

     _talon_
      Pile 1 W. Deal 1 card on _row_.

    _foundation_
      Pile 24 over 3 row N _row_. Build down in rank by same suit: N from base
      JACK. Center build QUEEN and S build KING after base JACK above. Play
      top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _foundation_: stack.SS_Foundation (base_rank=JACK,
      dir=-1, max_cards=11), Adela_Foundation (base_rank=QUEEN, max_cards=1),
      Adela_Foundation (base_rank=KING, max_cards=1)
    :cvar Stack _row_: stack.SS_RowStack  (max_move=1, dir=1)
    :cvar Hint __hint__: hint.CautiousDefaultHint
    """
    __info__ = dict(id=646, name='Adela', game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_LUCK, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _foundations_ = (Wrap(stack.SS_Foundation, base_rank=cs.JACK, dir=-1,
                          max_cards=11),
                     Wrap(Adela_Foundation, base_rank=cs.QUEEN, max_cards=1),
                     Wrap(Adela_Foundation, base_rank=cs.KING, max_cards=1),)
    _row_ = Wrap(stack.SS_RowStack, max_move=1, dir=1)
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=9, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + max(rows, l.NSUITS + 1) * l.XS, l.YM + 4 * l.YS)
        center = lambda p: (self.width - p * l.XS) / 2
        dx, y = l.XM + l.XS, l.YM
        for _foundation_ in self._foundations_:
            x = dx
            for i in l.SUITS:
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                x += l.XS
            y += l.YS
        x = center(rows)
        for i in range(rows):
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
            x += l.XS
        x, y = l.XM, l.YM + l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Toni(game.Game):
    """
    Finish cards on _foundation_ within 3 turn.

    _row_
      Pile 12 over 2 row E W _foundation_ Init deal 1 card. Build none. Play
      top. Base none.

     _talon_
      Pile 1 NW. Deal 1 card on _row_.

    _foundation_
      Pile 8 T-shape. Init deal horz base ACE and vert base KING. Build in
      rank by same suit: horz up and vert down. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon (max_rounds=3)
    :cvar Stack _foundation_: stack.SS_Foundation,
      stack.SS_Foundation (base_rank=KING, dir=-1)
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Hint __hint__: hint.CautiousDefaultHint
    """
    __info__ = dict(id=660, name='Toni', game_type=GT.TWO_DECK, decks=2,
                    redeals=2, skill_level=SL.MOSTLY_LUCK, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.DealRowRedealTalon, max_rounds=3)
    _foundations_ = (stack.SS_Foundation,
                     Wrap(stack.SS_Foundation, base_rank=cs.KING, dir=-1),)
    _row_ = stack.BasicRowStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 17 * l.XS / 2, l.YM + 4 * l.YS)
        for suit, dx in zip(l.SUIT, (2, 3, 5, 6,)):
            x, y = l.delta_coords(dx=dx)
            s['foundations'].append(self._foundations_[0](x, y, game=self,
                                                       suit=suit))
        x, y = l.XM + 4 * l.XS, l.YM
        for i in l.SUIT:  # Vert
            s['foundations'].append(self._foundations_[1](x, y, game=self,
                                                       suit=suit))
            y += l.YS
        for dx, dy in ((0, 0), (3, 0), (5, 0), (11, 0), (13, 0), (15, 0),
                       (0, 5), (3, 5), (5, 5), (11, 5), (13, 5), (15, 5),):
            x, y = l.delta_coords(dx=dx, dy=dy, fx=2, fy=2)
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        l.set_rounds_text(s['talon'], 'ne')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()


class Khedive(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 25 pyramid 5 high. Init deal 1 card. Build none. Play top. Gap deal
      1 card from _waste_. Base none.

     _talon_
      Pile 1 SW. Deal 1 card on _waste_.

    _waste_
      Pile 1 S of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 pile horz N _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.BasicRowStack
    """
    __info__ = dict(id=691, name='Khedive', game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_LUCK, version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = stack.BasicRowStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 10 * l.XS, l.YM + 5 * l.YS)
        x, y = l.XM, l.YM
        for i in l.SUIT:  # Horz
            xx = x
            for j in range(l.NDECKS):  # Horz step
                s['foundations'].append(self._foundation_(xx, y, game=self,
                                                       suit=i))
                xx += 6 * l.XS
            x += l.XS
        x, y = l.XM + 4 * l.XS, l.YM
        ddx = range(11)
        y = l.YM
        for i in range(5, 0, -1):  # Vert
            x = l.XM
            for dx in ddx[i:-i]:  # Horz
                x = l.XM + dx * l.XS / 2,
                s['rows'].append(self._row_(x, y, game=self))
            y += l.YS
        x, y = l.XM, self.height - 2 * l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'ne')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
           stack : Stack
             pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])


class Phalanx(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 15 pyramid 5 high. Init deal 1 card. Build 1 card. Play top.

     _talon_
      Pile 1 NW. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 pile horz E W _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.ReserveStack
    """
    __info__ = dict(id=730, name='Phalanx', game_type=GT.ONE_DECK, decks=1,
                    redeals=0, skill_level=SL.MOSTLY_LUCK, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _foundation_ = stack.SS_Foundation
    _row_ = stack.ReserveStack

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 8 * l.XS, l.YM + 5 * l.YS)
        y = l.YM
        for i in range(8, 3, -1):  # FIXME
            x = l.XM + i * l.XS / 2
            for j in range(i - 2):
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        for i, dx, dy in zip(l.SUIT, (3, 2, 13, 14), (3, 5,) * 2):
            x, y = l.XM + dx * l.XS / 2, l.YM + dy * l.YS / 2
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 's')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()


class Grandee(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 14 over 2 row. Init deal 1 card. Build down in rank by same suit.
      Play top.

     _talon_
      Pile 1 SE. Deal 1 card on _row_.

    _foundation_
      Pile 8 pile horz N _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _waste_: None
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.SS_RowStack (max_move=1)
    :cvar Hint __hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=742, name='Grandee', game_type=GT.TWO_DECK, decks=2,
                    redeals=0, skill_level=SL.BALANCED, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _waste_ = None
    _foundation_ = stack.SS_Foundation
    _row_ = Wrap(stack.SS_RowStack, max_move=1)
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=14, **kw):
        l, s = layout.Layout(self), self.s
        rows /= 2
        max_rows = max(l.NSUITS, rows)
        self.table_size = (l.XM + max_rows * l.XS, l.YM + 5 * l.YS)
        center = lambda p: l.XM + (max_rows - p) * l.XS / 2
        x, y = center(l.NSUITS), l.YM
        suits = l.SUITS[:]
        suits.sort()
        for i in suits:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        y += 3 * l.YS / 2
        for i in range(2):
            x = center(rows)
            for j in range(rows):
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                x += l.XS
            y += l.YS
        x, y = self.width - l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        if self._waste_:
            x -= l.XS
            s['wastes'].append(self._waste_(x, y, game=self))
            l.set_ncards_text(s['talon'], 'n')
            l.set_ncards_text(s['wastes'][-1], 'n')
        else:
            l.set_ncards_text(s['talon'], 'sw')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()


class Turncoats(Grandee):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 12 over 2 row. Init deal 1 card. Build either in rank by alt color.
      Play top. Gap deal 1 card from _talon_. Base none.

     _talon_
      Pile 1 SE.

    _foundation_
      Pile 8 pile horz N _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.Talon
    :cvar Stack _waste_: None
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.UD_AC_RowStack (base_rank=GAP)
    :cvar Hint __hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=743, name='Turncoats', game_type=GT.ONE_DECK, decks=1,
                    redeals=0, skill_level=SL.BALANCED, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.Talon
    _waste_ = None
    _row_ = Wrap(stack.UD_AC_RowStack, base_rank=cs.GAP)

    def _create_game(self, **kw):
        kw.setdefault('rows', 12)
        return super(Turncoats, self)._create_game(**kw)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Voracious(Grandee):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 12 over 2 row. Init deal 1 card. Build either in rank by same suit.
      Play top. Gap deal 1 card from _talon_. Base none.

     _talon_
      Pile 1 SE. Deal 1 card on _waste_.

    _waste_
      Pile 1 W of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 pile horz N _row_. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=1)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.SS_RowStack (base_rank=GAP, max_move=1)
    :cvar Hint __hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=744, name='Voracious', game_type=GT.TWO_DECK, decks=2,
                    skill_level=SL.BALANCED, version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=1)
    _waste_ = stack.WasteStack
    _row_ = Wrap(stack.SS_RowStack, base_rank=cs.GAP, max_move=1)

    def _create_game(self, **kw):
        kw.setdefault('rows', 12)
        return super(Voracious, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])


class DesertIsland(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 24 over 3. Init deal a card. Build 1 card. Play top.

     _talon_
      Pile 1 SE.

    _foundation_
      Pile 8 N _row_. Init deal base ACE. Build up in rank by same suit. Play
      Top. 1st 10 ranks finish.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _foundation_: stack.SS_Foundation (max_cards=10)
    :cvar Stack _row_: stack.ReserveStack
    """
    __info__ = dict(id=745, name='Desert Island', game_type=(GT.TWO_DECK |
                    GT.ORIGINAL), decks=2, redeals=0, skill_level=SL.BALANCED,
                    version='1.10fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.DealRowTalon
    _foundation_ = Wrap(stack.SS_Foundation, max_cards=10)
    _row_ = stack.ReserveStack

    def _create_game(self, rows=24, **kw):
        l, s = layout.Layout(self), self.s
        rows /= 3
        self.table_size = (l.XM + rows * l.XS, l.YM + 5 * l.YS)
        x, y = l.XM, l.YM
        suits = l.SUITS[:]
        suits.sort()
        for i in suits:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        y += l.YS
        for i in range(3):  # Vert
            x = l.XM
            for j in range(rows):
                s['rows'].append(self._row_(x, y, game=self))
                x += l.XS
            y += l.YS
        x, y = self.width - l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, self)
        l.set_ncards_text(s['talon'], 'sw')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[foundations] 1 ea.
          - s[rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['foundations'])


class CatherineTheGreat(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 over 2 col E W _foundation_. Init deal 1 card. Build up in rank
      alone wrap. Play top. Gap deal 1 card from _waste_. Base none.

     _talon_
      Pile 1 S. Deal 1 card on _waste_.

    _waste_
      Pile 1 E of _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 8 over 3 col N _row_. Init deal outer base QUEEN except heart,
      center base heart KING and QUEEN. Build in rank by same suit wrap: QUEEN
      down . KING up. Play none.
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=1)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.SS_Foundation (max_move=0,
      base_rank=QUEEN, dir=-1, mod=13), stack.SS_Foundation (max_move=0,
      base_rank=KING, mod=13, suit=2)
    :cvar Stack _row_: stack.RK_RowStack (dir=1, base_rank=GAP, max_move=1,
      mod=13)
    :cvar Hint __hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=761, name='Catherine the Great', game_type=GT.TWO_DECK,
                    decks=2, redeals=0, skill_level=SL.BALANCED,
                    version='2.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=1)
    _waste_ = stack.WasteStack
    _foundations_ = (Wrap(stack.SS_Foundation, max_move=0, base_rank=cs.QUEEN,
                          dir=-1, mod=13),
                     Wrap(stack.SS_Foundation, max_move=0, base_rank=cs.KING,
                          mod=13, suit=2),)
    _row_ = Wrap(stack.RK_RowStack, dir=1, base_rank=cs.GAP, max_move=1, mod=13)
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (3 * l.XM + 5 * l.XS, l.YM + 5 * l.YS)
        dx, dy = 2 * l.XM + l.XS, l.YM
        for xx, yy, suit in ((0, 2, 0), (0, 1, 0), (0, 0, 1), (2, 0, 1),
                             (1, 0, 2), (2, 1, 3), (2, 2, 3),):
            x, y = dx + xx * l.XS, dy + yy * l.YS
            s['foundations'].append(self._foundations_[0](x, y, game=self,
                                                       suit=suit))
        x, y = dx + l.XS, dy + l.YS
        s['foundations'].append(self._foundations_[1](x, y, game=self))
        x = l.XM
        for i in range(2):  # Horz
            y = l.YM
            for i in range(5):  # Vert
                s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                y += l.YS
            x += 2 * l.XM + 4 * l.XS
        x, y = 2 * l.XM + 3 * l.XS / 2, l.YM + 4 * l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], 'n')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return QUEEN club spade diamond and KING heart on top by
        rank, suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneQcsdKh)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - [foundations] 1 ea.
          - [rows] 1 ea.

        .. \_start([]) -> None
        """
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])


PROVIDES = [CatherineTheGreat, DesertIsland, Voracious, Turncoats, Grandee,
            Phalanx, TwoRings, Khedive, Toni, Adela, CircleEight,
            PicturePatience, RoyalAids, Marshal, CornerSuite, SixesAndSevens,
            Patriarchs, Matrimony, LadyOfTheManor, IdleAces, Contradance,
            CaptiveQueens, Boudoir, SultanPlus, Sultan]